package org.goodsl.mdd

/**
 * Created with IntelliJ IDEA.
 * User: Simon Xiao
 * Date: 12-10-27
 * Time: 下午4:47
 * To change this template use File | Settings | File Templates.
 */
class Box {

    BoxInfo boxInfo
    Box inTheBox


    protected void setInTheBox(Box box) {
        inTheBox = box
    }

    SegInBox segInBox

    protected void setBoxSegInTheBox(SegInBox boxSeg) {
        segInBox = boxSeg
    }

    protected void setBoxInfo(BoxInfo info) {
        this.boxInfo = info
    }

    public Object propertyMissing(String name) {
        Object result =allValues.get(name)
        if(result == null && inTheBox!=null){
            result = inTheBox.calcSegValue(segInBox.segName,name)
            allValues.put(name,result)
        }
        return result

    }

    public ValidResult verify(){
        return boxInfo.verify(this)
    }

    public ValidResult verify(String segName){
        return boxInfo.verify(this,segName)
    }

    public Object propertyMissing(String name, Object newValue) {
        return allValues.put(name, newValue);
    }

    private HashMap<String, Object> allValues = new HashMap<String, Object>(4)


    Object calcSegValue(String segName) {
        return calcSegValue(segName, "value")
    }

    Object calcSegValue(String segName, String dependAspectName) {
        Closure calc = getValueCalculator(segName,dependAspectName)
        if(calc == null) return null
        Object boxSegInstance = this."$segName"
        Object newValue = calc(boxSegInstance)
        return newValue

    }

    /**
     * get the seg instance by seg name
     * @param segName
     * @return  the seg instance
     */
    public Seg getSeg(String segName){
        try{
            Seg seg = this."$segName"
            return  seg
        }
        catch (Exception e){
            return null
        }
    }

    public HashMap<String,Object> getSegValues(){
        HashMap<String,Object> values = new HashMap<String,Object>()
        boxInfo.boxSegList.each {
             values.put(it.segName,this.getSeg(it.segName).getValue())
        }

        return values
    }

    void updateAllDependOn(String segName){
        SegInBox segInfo = boxInfo.boxSegMap[segName]
        if(segInfo == null) return
        segInfo.dependBy.keySet().each {String dependAspectName ->
            ArrayList<SegInBox> affectedSegInfos = segInfo.dependBy[dependAspectName]
            affectedSegInfos.each { SegInBox affectedSegInfo ->
                String affectedSegName = affectedSegInfo.segName
                Object newValue = calcSegValue(affectedSegName,dependAspectName)
                Object segInstance = this."$affectedSegName"
                segInstance."$dependAspectName" = newValue
            }

        }
    }

    private HashMap<String,HashMap<String,Closure>> calculators =
        new HashMap<String,HashMap<String,Closure>>(4)
    /**
     * 类型解析中获取到的求值闭包并不能直接用于计算
     * 需要先克隆，设置进去正确的thisObject,delegate,owner
     * 克隆后得到的闭包缓存在Box对象实例中，避免下次计算时
     * 重复创建。闭包内再定义的闭包也要克隆，克隆结果直接缓存在
     * 父亲闭包中
     * @param segName
     * @param dependAspectName
     * @return
     */
    private Closure getValueCalculator(String segName, String dependAspectName) {
        Closure calc = null
        SegInBox segInfo = boxInfo.boxSegMap[segName]
        HashMap<String,Closure> segCalculators = calculators[segName]
        if(segCalculators==null){
            if (segInfo == null) return null
            segCalculators = new HashMap<String,Closure>(4)
            calculators[segName] = segCalculators
        }
        calc = segCalculators[dependAspectName]
        if(calc == null){
            Closure code = segInfo.dependSegCode[dependAspectName]
            if(code ==null) return null
            DependExecuteDelegate delegate = new DependExecuteDelegate(this)
            calc = code.rehydrate(delegate,this,this)  //克隆一个新的
            calc.resolveStrategy = Closure.DELEGATE_FIRST
            segCalculators[dependAspectName] = calc
        }
        return calc
    }
}
